package com.zecan.blog.websocket;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.zecan.blog.dao.UserMapper;
import com.zecan.blog.entity.BlogUser;
import com.zecan.blog.rabbitmq.BlogMqProducer;
import com.zecan.blog.rabbitmq.event.ChatRoomEvent;
import com.zecan.blog.utils.BlogConstant;
import com.zecan.blog.utils.SpringUtils;
import com.zecan.blog.utils.StringUtils;
import com.zecan.blog.vo.chatroom.ChatRoom;
import com.zecan.blog.vo.chatroom.ChatRoomMsgVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @ClassName rver
 * @Description
 * @date 2022/11/21 10:43
 * @Version 1.0
 */

@Component
@Slf4j
@ServerEndpoint("/wsBlog/{userId}")
public class WebSocketServer {


    // rabbitmq生产者 的注入
    private BlogMqProducer blogMqProducer = SpringUtils.getBean(BlogMqProducer.class);

    private UserMapper userMapper = SpringUtils.getBean(UserMapper.class);

    // 每个用户的session连接
    private Session session;

    // 每个用户的id
    private String userId;

    // 使用 copyOnWriteArraySet  存储每个websocket连接  当前类型是可以保持不重复, 可以用于广播消息传递
    private static CopyOnWriteArrayList<WebSocketServer> webSockets = new CopyOnWriteArrayList<>();

    // 使用ConcurrentHashMap 保存每个用户连接信息, 用于对单点消息 或者 群聊消息
    private static ConcurrentHashMap<String, Session> sessionPool = new ConcurrentHashMap<>();

    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        try {
            // 如果当前存在连接则直接将之前的连接关闭
            for (WebSocketServer webSocket : webSockets) {
                if (webSocket.userId.equals(userId)) {
                    webSockets.remove(webSocket);
                    sessionPool.remove(userId);
                }
            }
            // 添加连接
            this.session = session;
            this.userId = userId;
            webSockets.add(this);
            sessionPool.put(userId, session);
            // 通知所有用户 有人上线了
            sendOnOrOff(1);
            log.info(("【websocket消息】有新的连接，总数为:" + webSockets.size()));
        } catch (Exception e) {
            log.error("【websocket】连接异常:" + e.getMessage());
        }
    }

    @OnClose
    public void onClose() {
        try {
            webSockets.remove(this);
            sessionPool.remove(this.userId);

            // 通知所有用户 有人下线了
            sendOnOrOff(0);
            log.info(("【websocket消息】公共聊天室连接断开，总数为:" + webSockets.size()));
        } catch (Exception e) {
            log.error("【websocket】公共聊天室关闭连接异常:" + e.getMessage());
        }
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message
     */
    @OnMessage
    public void onMessage(String message) {
        cn.hutool.json.JSON parse = JSONUtil.parse(message);

        // 获取用户发送的信息
        String userSendMsg = parse.getByPath("message", String.class);
        Integer type = parse.getByPath("type", Integer.class);
        // 0 群发 1 单独
        if (type.equals(0)) {
            sendAllMessage(userSendMsg);
        } else if (type.equals(1)) {
            // 单独发消息
            // 获取用户发送的信息
            Integer toUserId = parse.getByPath("toUserId", Integer.class);
            Integer fromUserId = parse.getByPath("fromUserId", Integer.class);
            String avatarUrl = parse.getByPath("toUserAvatarUrl", String.class);
            String nickname = parse.getByPath("nickname", String.class);
            // 单独发消息
            sendMessage(fromUserId, toUserId, userSendMsg, avatarUrl, nickname);
        }

        // sendAllMessage(message);

    }


    // 单独发送消息
    public void sendMessage(Integer fromUserId, Integer toUserId, String userSendMsg, String avatarUrl, String nickname) {
        // 获取用户对应的连接
        Session session = sessionPool.get(String.valueOf(toUserId));

        if (session != null && session.isOpen()) {
            try {
                log.info("【websocket消息】 单点消息:" + userSendMsg);
                // 发送消息
                ChatRoom chatRoom = ChatRoom.builder().message(userSendMsg).userId(fromUserId).leftOrRight(1).avatar(avatarUrl).username(nickname).build();
                ChatRoomMsgVo build = ChatRoomMsgVo.builder().chatRoom(chatRoom).type(1).isNewMsg(true).build();
                // 发送给消息队列添加聊天记录
                blogMqProducer.sendChatMessage(BlogConstant.RABBITMQ_EXCHANGE, BlogConstant.RABBITMQ_CHATROOM_ROUTING_KEY,
                        ChatRoomEvent.builder()
                                .chatRoomType(1)
                                .receiveUserId(toUserId)
                                .content(userSendMsg)
                                .userId(fromUserId)
                                .status(0)
                                .users(StringUtils.concatChattingUsers(fromUserId, toUserId))
                                .build());
                // 发送消息
                session.getAsyncRemote().sendText(JSONUtil.toJsonStr(build));
            } catch (Exception e) {
                e.printStackTrace();
            }
            // 如果不在线则直接添加聊天记录即可
        } else {
            blogMqProducer.sendChatMessage(BlogConstant.RABBITMQ_EXCHANGE, BlogConstant.RABBITMQ_CHATROOM_ROUTING_KEY,
                    ChatRoomEvent.builder()
                            .chatRoomType(1)
                            .receiveUserId(toUserId)
                            .content(userSendMsg)
                            .userId(fromUserId)
                            .status(0)
                            .users(StringUtils.concatChattingUsers(fromUserId, toUserId))
                            .build());
        }
    }

    //发送消息 群聊 多人
    public void sendMoreMessage(Integer[] ids, String message) {

    }

    //广播消息  其中执行方法的是
    public void sendAllMessage(String message) {
        String username = null;
        String avatar = null;

        // 如果只有自己一个人 那么就直接保存到redis中
        if (webSockets.size() == 1) {
            BlogUser userInfo = userMapper.findUserAvatarAndUsernameById(this.userId);
            username = userInfo.getUserNickname();
            avatar = userInfo.getUserAvatarUrl();
            blogMqProducer.sendChatMessage(BlogConstant.RABBITMQ_EXCHANGE, BlogConstant.RABBITMQ_CHATROOM_ROUTING_KEY,
                    ChatRoomEvent.builder().leftOrRight("0")
                            .content(message).chatRoomType(0).
                            userId(Integer.parseInt(this.userId)).avatar(avatar).username(username).build()
            );
            return;
        }
        for (WebSocketServer webSocket : webSockets) {
            // 如果当前没有连接 直接跳过
            if (!webSocket.session.isOpen()) {
                continue;
            }
            // 获取用户信息并封装到返回的data中
            BlogUser userInfo = userMapper.findUserAvatarAndUsernameById(this.userId);
            username = userInfo.getUserNickname();
            avatar = userInfo.getUserAvatarUrl();
            // 只有在不是自己的时候才发送消息
            if (!Objects.equals(webSocket.userId, this.userId)) {
                ChatRoom chatRoom = ChatRoom.builder().message(message).leftOrRight(1).avatar(avatar).username(username).build();
                ChatRoomMsgVo build = ChatRoomMsgVo.builder().chatRoom(chatRoom).type(0).isNewMsg(true).build();
                webSocket.session.getAsyncRemote().sendText(JSON.toJSONString(build));
                // 将发送消息的用户信息封装 通过rabbitmq 保存在redis中
                blogMqProducer.sendChatMessage(BlogConstant.RABBITMQ_EXCHANGE, BlogConstant.RABBITMQ_CHATROOM_ROUTING_KEY,
                        ChatRoomEvent.builder().leftOrRight("1")
                                .chatRoomType(0)
                                .content(message).userId(Integer.parseInt(this.userId)).avatar(avatar).username(username).build()
                );
            }
        }
        log.info("【websocket消息】公共聊天室收到客户端消息:" + message);

    }

    // 通知所有人有人上线或者下线
    public void sendOnOrOff(Integer onOrOff) {
        // 1 代表有人上线 , 0 代表有人下线了
        for (WebSocketServer webSocket : webSockets) {
            if (webSocket.session.isOpen() && webSocket.session != this.session) {
                // 获取用户信息并封装到返回的data中
                BlogUser userInfo = userMapper.findUserAvatarAndUsernameById(this.userId);
                ChatRoom chatRoom = ChatRoom.builder().username(userInfo.getUserNickname()).onOrOff(onOrOff).build();

                // 封装对应的vo 其中的
                ChatRoomMsgVo build = ChatRoomMsgVo.builder().chatRoom(chatRoom).type(0).isNewMsg(false).build();
                webSocket.session.getAsyncRemote().sendText(JSON.toJSONString(build));
            }
        }
    }

    @OnError
    public void onError(Throwable error) {
        System.out.println("onError......" + error.getMessage());

    }

}
